/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "TransactionDatabase.h"

#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTextCodec>
#include <QtCore/QDateTime>

#include "Utilities.h"
#include "StockDatabase.h"

namespace QSThink
{
	TransactionDatabase::TransactionDatabase()
	{

	}

	Transaction1Day* TransactionDatabase::getDayTransaction(QString symbol,int day)
	{
		TransactionsMap::iterator it=_transactions.find(symbol);
		if(it!=_transactions.end()){
			StockTransactions* st= it->second;
			return st->get(day);
		}

		StockTransactions* trans=new StockTransactions(symbol);

		_transactions.insert(TransactionsMap::value_type(symbol,trans));
		return trans->get(day);

	}

	Transaction1Day* TransactionDatabase::getDayTransaction( QString symbol,QDate date )
	{

		int day=Utilities::getDaysFrom1900(date);
		return getDayTransaction(symbol,day);

	}

	QDate TransactionDatabase::getStartDate( QString symbol )
	{
		return StockDatabase::getSingleton().getStockPublicDate(symbol);
	}

	QDate TransactionDatabase::getEndDate( QString symbol )
	{
		return QDate::currentDate();

	}

	StockTransactions* TransactionDatabase::getTransactions( QString symbol)
	{
		TransactionsMap::iterator it=_transactions.find(symbol);
		if(it!=_transactions.end()){
			StockTransactions* st= it->second;
			return st;
		}

		StockTransactions* trans=new StockTransactions(symbol);

		_transactions.insert(TransactionsMap::value_type(symbol,trans));
		return trans;

	}

	int TransactionDatabase::getStartDay( QString symbol )
	{
		return StockDatabase::getSingleton().getStockPublicDay(symbol);

	}

	int TransactionDatabase::getEndDay( QString symbol )
	{
		QDate date=QDate::currentDate();
		return Utilities::getDaysFrom1900(date);

	}



	/*

	DayTransaction* TransactionDatabase::addDayTransaction( int day,int symbol,QByteArray bytes )
	{
		std::stringstream ss;
		ss << day;
		ss << "_";
		ss << symbol;
		std::string id=ss.str();

		DayTransaction* dayTrans=0;
		DayTransactionsMap::iterator it=_dayTransactions.find(id);
		if(it!=_dayTransactions.end()){
			//find
			dayTrans= it->second;
		}else{
			dayTrans=new DayTransaction(day,symbol);
			_dayTransactions.insert(DayTransactionsMap::value_type(id,dayTrans));
		}

		if(dayTrans->dataReady()){
			return dayTrans;
		}
		
		
		QString str=QTextCodec::codecForName("GB2312")->toUnicode(bytes);
		QStringList lines =str.split("\n");

		if(lines[0].startsWith("<script")){

			dayTrans->setHasData(false);
			return dayTrans;
		}

		int cnt=lines.count();

		//start from 1 because 0 is meta info we don't need
		for(int i=1;i<cnt;i++){
			QString line=lines[i];
			Transaction* trans=parseLine(line);
			if(trans){
				dayTrans->addTransaction(trans);
			}
		}

		//persist data when all data is parsed
		dayTrans->persist();
		return dayTrans;
	}

	Transaction* TransactionDatabase::parseLine( QString line )
	{
		QStringList lines =line.split("\t");
		int cnt=lines.count();
		if(cnt!=6){
			return 0;
			//QList<QString> = [6]("14:59:45","3.29","--","349","114821","B/S")

		}

		int time=Utilities::getTimeOffset(lines[0]);
		float price=lines[1].toFloat();

		float price100=price*100.0f;//unit is feng
		int volume=lines[3].toInt();
		int amount=lines[4].toInt();
		
		int type=Utilities::getTransType(lines[5]);

		Transaction* trans=new Transaction(time,price100,volume,amount,type);
		return trans;
	}

	DayTransaction* TransactionDatabase::getDayTransaction( int day,int symbol )
	{

		std::stringstream ss;
		ss << day;
		ss << "_";
		ss << symbol;
		std::string id=ss.str();

		DayTransaction* dayTrans=0;
		DayTransactionsMap::iterator it=_dayTransactions.find(id);
		if(it!=_dayTransactions.end()){
			//find
			dayTrans= it->second;
		}else{
			dayTrans=new DayTransaction(day,symbol);
			_dayTransactions.insert(DayTransactionsMap::value_type(id,dayTrans));
		}

		return dayTrans;
	}

	
	*/

	int StockTransactions::getOpenDayCount( int startDay,int endDay )
	{
		int cnt=0;

		//cnt=endDay-startDay+1;//a
		for(int i=startDay;i<=endDay;++i){
			std::map<int,Transaction1Day*>::iterator it=_transactions.find(i);
			if(it!=_transactions.end()){
				cnt++;//find
			}
		}

		return cnt;
	}

	Transaction1Day* StockTransactions::getDay( int day )
	{
		std::map<int,Transaction1Day*>::iterator it=_transactions.find(day);
		if(it!=_transactions.end()){
			Transaction1Day* trans=it->second;
			if(trans){
				//trans->
			}
			return trans;
		}
		return 0;
	}

	Transaction1Day* StockTransactions::get( int day )
	{
		std::map<int,Transaction1Day*>::iterator it=_transactions.find(day);
		if(it!=_transactions.end()){
			return it->second;
		}
		Transaction1Day* trans=new Transaction1Day(_symbol,day);
		_transactions.insert(std::map<int,Transaction1Day*>::value_type(day,trans));
		return trans;
	}

	StockTransactions::StockTransactions( QString symbol )
		: _loaded(false)
	{
		_symbol=symbol;
		//should load quick search meta data file?

		if(_loaded){
			return;
		}

		
		QString fileName=_symbol+"/"+_symbol+".day";
		QFile file(fileName);
		if (!file.open(QFile::ReadOnly)){
			_loaded=false;
			return ;
		}

		QByteArray bytes=file.readAll();
		char* data=bytes.data();
		int index=0;

		int cnt=*(int*)(data+index);
		index+=sizeof(cnt);

		for(int i=0;i<cnt;++i){

			int day=*(int*)(data+index);
			index+=sizeof(day);

			float balancePrice=*(float*)(data+index);
			index+=sizeof(balancePrice);

			int openPrice=*(int*)(data+index);
			index+=sizeof(openPrice);

			int closePrice=*(int*)(data+index);
			index+=sizeof(closePrice);

			int highPrice=*(int*)(data+index);
			index+=sizeof(highPrice);

			int highIndex=*(int*)(data+index);
			index+=sizeof(highIndex);

			int lowPrice=*(int*)(data+index);
			index+=sizeof(lowPrice);

			int lowIndex=*(int*)(data+index);
			index+=sizeof(lowIndex);

			int volume=*(int*)(data+index);
			index+=sizeof(volume);

			int amount=*(int*)(data+index);
			index+=sizeof(amount);

			Transaction1Day *trans=0;
			std::map<int,Transaction1Day*>::iterator it= _transactions.find(day);
			if(it!=_transactions.end()){
				trans=it->second;
				trans->setStatistics(balancePrice,openPrice,closePrice,highPrice,highIndex,lowPrice,lowIndex,volume,amount);
			}else{
				trans=new Transaction1Day(_symbol,day);
				trans->setStatistics(balancePrice,openPrice,closePrice,highPrice,highIndex,lowPrice,lowIndex,volume,amount);
				_transactions.insert(std::map<int,Transaction1Day*>::value_type(day,trans));
			}


		}

		_loaded=true;

	}

	void StockTransactions::loadTreeData(){
		if(_loaded){
			return;
		}
		QString fileName=_symbol+"/"+_symbol+".day";
		QFile file(fileName);
		if (!file.open(QFile::ReadOnly)){
			_loaded=false;
			buidTreeData();//heavy calc

			_loaded=true;
			return ;
		}


		QByteArray bytes=file.readAll();
		char* data=bytes.data();
		int index=0;

		int cnt=*(int*)(data+index);
		index+=sizeof(cnt);

		for(int i=0;i<cnt;++i){

			int day=*(int*)(data+index);
			index+=sizeof(day);

			float balancePrice=*(float*)(data+index);
			index+=sizeof(balancePrice);

			int openPrice=*(int*)(data+index);
			index+=sizeof(openPrice);

			int closePrice=*(int*)(data+index);
			index+=sizeof(closePrice);

			int highPrice=*(int*)(data+index);
			index+=sizeof(highPrice);

			int highIndex=*(int*)(data+index);
			index+=sizeof(highIndex);

			int lowPrice=*(int*)(data+index);
			index+=sizeof(lowPrice);

			int lowIndex=*(int*)(data+index);
			index+=sizeof(lowIndex);

			int volume=*(int*)(data+index);
			index+=sizeof(volume);

			int amount=*(int*)(data+index);
			index+=sizeof(amount);

			Transaction1Day *trans=0;
			std::map<int,Transaction1Day*>::iterator it= _transactions.find(day);
			if(it!=_transactions.end()){
				trans=it->second;
				trans->setStatistics(balancePrice,openPrice,closePrice,highPrice,highIndex,lowPrice,lowIndex,volume,amount);
			}else{
				trans=new Transaction1Day(_symbol,day);
				trans->setStatistics(balancePrice,openPrice,closePrice,highPrice,highIndex,lowPrice,lowIndex,volume,amount);
				_transactions.insert(std::map<int,Transaction1Day*>::value_type(day,trans));
			}


		}

		_loaded=true;
	}

	void StockTransactions::buidTreeData()
	{
		int startDay=TransactionDatabase::getSingleton().getStartDay(_symbol);
		int endDay=TransactionDatabase::getSingleton().getEndDay(_symbol);


		QString fileName=_symbol+"/"+_symbol+".day";
		QFile file(fileName);
		if (!file.open(QFile::WriteOnly)){
			return;
		}

		std::vector<Transaction1Day*> list;

		for(int i=startDay;i<=endDay;++i){

			Transaction1Day* trans=0;
			std::map<int,Transaction1Day*>::iterator it=_transactions.find(i);
			if(it!=_transactions.end()){
				trans= it->second;
			}else{
				trans=new Transaction1Day(_symbol,i);
				_transactions.insert(std::map<int,Transaction1Day*>::value_type(i,trans));

			}
			
			if(trans->loadData()){
				list.push_back(trans);
			}
		}

		int cnt=list.size();
		
		QByteArray bytes;
		bytes.append((char*)&cnt,sizeof(cnt));
		for(int i=0;i<cnt;++i){
			Transaction1Day* t=list[i];
			int day=t->day();
			float balancePrice=t->balancePrice();
			int openPrice=t->openPrice();
			int closePrice=t->closePrice();
			int highPrice=t->highPrice();
			int highIndex=t->highIndex();
			int lowPrice=t->lowPrice();
			int lowIndex=t->lowIndex();
			int volume=t->volume();
			int amount=t->amount();

			//this is not forward answer authority
			bytes.append((char*)&day,sizeof(day));
			bytes.append((char*)&balancePrice,sizeof(balancePrice));
			bytes.append((char*)&openPrice,sizeof(openPrice));
			bytes.append((char*)&closePrice,sizeof(closePrice));
			bytes.append((char*)&highPrice,sizeof(highPrice));
			bytes.append((char*)&highIndex,sizeof(highIndex));
			bytes.append((char*)&lowPrice,sizeof(lowPrice));
			bytes.append((char*)&lowIndex,sizeof(lowIndex));
			bytes.append((char*)&volume,sizeof(volume));
			bytes.append((char*)&amount,sizeof(amount));


		}

		file.write(bytes);


	}

	double StockTransactions::getHighestPriceEx( QDate startDate,QDate endDate )
	{
		int startDay=Utilities::getDaysFrom1900(startDate);
		int endDay=Utilities::getDaysFrom1900(endDate);


		int cnt=_transactions.size();

		std::map<int,Transaction1Day*>::iterator it=_transactions.begin();
		std::map<int,Transaction1Day*>::iterator itEnd=_transactions.end();

		double price=0;
		bool hasdata=false;
		for (;it!=itEnd;++it)
		{
			int day=it->first;

			if (day>=startDay&&day<=endDay)
			{
				if(!it->second->statisticed()){
					continue;

				}

				double p=it->second->highPriceEx();
				if(!hasdata){
					price=p;
					hasdata=true;

				}else if (p>price){
					price=p;
				}

				
			}
		}
		if(!hasdata){
			return 0;
		}
		return price;
	}

	double StockTransactions::getLowestPriceEx( QDate startDate,QDate endDate )
	{
		int startDay=Utilities::getDaysFrom1900(startDate);
		int endDay=Utilities::getDaysFrom1900(endDate);


		int cnt=_transactions.size();

		std::map<int,Transaction1Day*>::iterator it=_transactions.begin();
		std::map<int,Transaction1Day*>::iterator itEnd=_transactions.end();

		double price=0;
		bool hasdata=false;
		for (;it!=itEnd;++it)
		{
			int day=it->first;

			if (day>=startDay&&day<=endDay)
			{
				if(!it->second->statisticed()){
					continue;

				}

				double p=it->second->lowPriceEx();
				
				if(!hasdata){
					price=p;
					hasdata=true;
				}else if (p<price){
					
					price=p;
					
				}

			}
		}

		if(!hasdata){
			return 0;
		}

		return price;
	}


}
